<!DOCTYPE html>
<html lang="en">
<head>
<title>Oimo.js worker test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=11" />
<link rel="shortcut icon" href="./assets/img/favicon.ico">
<link rel="stylesheet" href="css/demo.css">

<style>
    * {  margin: 0; padding: 0; border: 0;}
    body { background-color: #585858; overflow: hidden; color: #eeeeee; font-family: monospace; font-size: 12px; }
    input{ margin:0; padding:4px; }
    #interface{ position: absolute; left:10px; top:10px; width:456px; height:20px; }
    #info{ pointer-events:none; position: absolute; left:10px; top:50px; width: 400px; height: 400px; }
</style>
</head>
<body>
<div id='container'></div>
<div id="info">
    <a href="https://github.com/lo-th/Oimo.js">Oimo.js</a> web worker example
</div>
<a href="https://github.com/lo-th/Oimo.js"><img style="position: absolute; top: 0; right: 0; border: 0;" src="assets/img/ribbon.png" alt="Fork me on GitHub"></a> 

<script src="js/libs/three.min.js"></script>

<!-- Worker script, will be run in separate thread -->
<script id="worker1" type="javascript/worker">    
    var world;
    var minfo;
    var fps = 0;
    var f = [0,0,0];
    var body = [];
    self.onmessage = function(e) {

        if (e.data.oimoUrl && !world) {
            // Load oimo.js
            importScripts( e.data.oimoUrl );

            // Init physics
            world = new OIMO.World( { timestep:e.data.dt, iterations:8, broadphase:2, worldscale:1, random:true, info:false } );

            // Ground plane
            var ground = world.add({size:[200, 20, 200], pos:[0,-10,0]});
            
            var N = e.data.N;

            minfo = new Float32Array( N * 8 );

            var x, z;
            for(var i=0; i!==N; i++){
                x = -2 + Math.random()*4;
                z = -2 + Math.random()*4;
                if(N < N*0.5) body[i] = world.add({type:'sphere', size:[0.25], pos:[x,(0.5*i)+0.5,z], move:true});
                else body[i] = world.add({type:'box', size:[0.5,0.5,0.5], pos:[x,((0.5*i)+0.5),z], move:true});
            }

            setInterval( update, e.data.dt*1000 );
        }

    };

    var update = function() {

        // Step the world
        world.step();

        var n;

        body.forEach( function ( b, id ) {

            n = 8 * id;

            if( b.sleeeping ){

                minfo[ n + 7 ] = 1;

            } else {

                minfo[ n + 7 ] = 0;
                b.getPosition().toArray( minfo, n );
                b.getQuaternion().toArray( minfo, n+3 );

            }

        });

        f[1] = Date.now();
        if (f[1]-1000>f[0]){ f[0]=f[1]; fps=f[2]; f[2]=0; } f[2]++;

        self.postMessage({ perf:fps, minfo:minfo })

    }



</script>

<script>
    // Parameters
    var dt = 1/60;
    var N= 666;
    var ToRad = Math.PI / 180;
    var info = document.getElementById("info");

    // navigation var 
    var camPos = { horizontal: 90, vertical: 30, distance: 100, automove: false };
    var mouse = { ox:0, oy:0, h:0, v:0, mx:0, my:0, down:false, over:false, moving:true };

    var fps=0, time, time_prev=0, fpsint = 0;

    var minfo = new Float32Array(N*8);
    var oimoInfo = 0;

    // Create a blob for the inline worker code
    var blob = new Blob([document.querySelector('#worker1').textContent], {type : 'text/javascript'});

    // Create worker
    var worker = new Worker(window.URL.createObjectURL(blob));
    worker.postMessage = worker.webkitPostMessage || worker.postMessage;

    worker.onmessage = function(e) {

        // stat
        oimoInfo = e.data.perf;

        // Get fresh data from the worker
        minfo = e.data.minfo;

        var n = 0;

        meshes.forEach( function ( b, id ) {

            n = id*8;
            if(minfo[n+7]!==1){
                b.position.fromArray( minfo, n);
                b.quaternion.fromArray( minfo, n+3 );
            }



        });

    }

    var container, camera, scene, renderer;
    var meshes=[];

    init();
    animate();

    function init() {

        renderer = new THREE.WebGLRenderer( {precision: "mediump", antialias:true, alpha: true } );
        renderer.setSize( window.innerWidth, window.innerHeight );
        renderer.setClearColor( 0x000000, 0 );
        renderer.autoClear = false;

        container = document.getElementById("container");
        container.appendChild( renderer.domElement );

        //renderer.gammaInput = true;
        //renderer.gammaOutput = true;
        renderer.shadowMap.enabled = true;

        // scene
        scene = new THREE.Scene();

        // camera
        camera = new THREE.PerspectiveCamera( 30, window.innerWidth / window.innerHeight, 0.5, 10000 );
        camera.position.set( 0, 30, 100 );
        center = new THREE.Vector3();
        moveCamera();

        scene.add( camera );

        // lights
        var light, materials;
        scene.add( new THREE.AmbientLight( 0x303030 ) );

        light = new THREE.DirectionalLight( 0xffffff, 1.5 );
        var d = 40;

        light.position.set( d, d, d );

        light.castShadow = true;
        light.shadow.camera = new THREE.OrthographicCamera( -d, d, d, -d,  d, d*3 );
        light.shadow.bias = 0.0001;
        light.shadow.mapSize.width = light.shadow.mapSize.height = 1024;

        scene.add( light );

        var material = new THREE.MeshBasicMaterial( { color: 0xFFFFFF, transparent:true, opacity:0.06 } );
        var cubeMaterial = new THREE.MeshPhongMaterial( { color: 0xCC8888 } );
        var sphereMaterial = new THREE.MeshPhongMaterial( { color: 0x8888CC } );

        //geometry = THREE.BufferGeometryUtils.fromGeometry( new THREE.PlaneGeometry( 400, 400, 1, 1 ) );
        geometry = new THREE.BufferGeometry();
        geometry.fromGeometry( new THREE.PlaneGeometry( 200, 200, 1, 1 ) );
        
        var mesh = new THREE.Mesh( geometry, material );
        mesh.castShadow = false;
        mesh.receiveShadow = true;
        mesh.rotation.x = -90 * ToRad;
        scene.add( mesh );
           
        // cubes
        //var cubeGeo = THREE.BufferGeometryUtils.fromGeometry(new THREE.BoxGeometry( 0.5,  0.5,  0.5, 1, 1, 1));
        var cubeGeo = new THREE.BufferGeometry();
        cubeGeo.fromGeometry( new THREE.BoxGeometry( 0.5,  0.5,  0.5, 1, 1, 1) );
        

        for(var i=0; i<N*0.5; i++){
            mesh = new THREE.Mesh( cubeGeo, cubeMaterial );
            mesh.castShadow = true;
            mesh.receiveShadow = true;
            meshes.push(mesh);
            scene.add( mesh );
        }

        // sphere
        //var sphereGeo = THREE.BufferGeometryUtils.fromGeometry(new THREE.SphereGeometry( 0.25, 16, 8));
        var sphereGeo = new THREE.BufferGeometry();
        sphereGeo.fromGeometry( new THREE.SphereGeometry( 0.25, 10, 8) );
        

        for(var i=0; i<N*0.5; i++){
            mesh = new THREE.Mesh( sphereGeo, sphereMaterial );
            mesh.castShadow = true;
            mesh.receiveShadow = true;
            meshes.push(mesh);
            scene.add( mesh );
        }



        window.addEventListener( 'resize', onWindowResize, false );
        container.addEventListener( 'mousemove', onMouseMove, false );
        container.addEventListener( 'mousedown', onMouseDown, false );
        container.addEventListener( 'mouseout', onMouseUp, false );
        container.addEventListener( 'mouseup', onMouseUp, false );
        container.addEventListener( 'mousewheel', onMouseWheel, false );
        container.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox

        worker.postMessage({  N:N, dt:dt, oimoUrl:document.location.href.replace(/\/[^/]*$/,"/") + "../build/oimo.js" } );
    }

    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize( window.innerWidth, window.innerHeight );
    }

    function animate() {
        requestAnimationFrame( animate );
        render();
        displayInfo();
    }

    function displayInfo(){
        time = Date.now();
        if (time - 1000 > time_prev) {
            time_prev = time; fpsint = fps; fps = 0;
        } fps++;

        var info =[
            "Oimo.js DEV.1.1.1a<br><br>",
            "Physics: " + oimoInfo +" fps<br>",
            "Render: " + fpsint +" fps<br>"
        ].join("\n");
        document.getElementById("info").innerHTML = info;
    }

    function render() {
        renderer.render( scene, camera );
    }

    // MATH

    function Orbit(origine, horizontal, vertical, distance) {
        var p = new THREE.Vector3();
        var phi = vertical*ToRad;
        var theta = horizontal*ToRad;
        p.x = (distance * Math.sin(phi) * Math.cos(theta)) + origine.x;
        p.z = (distance * Math.sin(phi) * Math.sin(theta)) + origine.z;
        p.y = (distance * Math.cos(phi)) + origine.y;
        return p;
    }

    // MOUSE & NAVIGATION
    function moveCamera() {
        camera.position.copy(Orbit(center, camPos.horizontal, camPos.vertical, camPos.distance));
        camera.lookAt(center);
    }

    function onMouseDown(e) {
        e.preventDefault();
        mouse.ox = e.clientX;
        mouse.oy = e.clientY;
        mouse.h = camPos.horizontal;
        mouse.v = camPos.vertical;
        mouse.down = true;
    }

    function onMouseUp(e) {
        mouse.down = false;
        document.body.style.cursor = 'auto';
    }

    function onMouseMove(e) {
        e.preventDefault();
        if (mouse.down ) {
            document.body.style.cursor = 'move';
            camPos.horizontal = ((e.clientX - mouse.ox) * 0.3) + mouse.h;
            camPos.vertical = (-(e.clientY - mouse.oy) * 0.3) + mouse.v;
            moveCamera();
        }
    }

    function onMouseWheel(e) {
        var delta = 0;
        if(e.wheelDeltaY){delta=e.wheelDeltaY*0.01;}
        else if(e.wheelDelta){delta=e.wheelDelta*0.05;}
        else if(e.detail){delta=-e.detail*1.0;}
        camPos.distance-=(delta*10);
        moveCamera();   
    }

 </script>
</body>
</html>